package gui.std.games.battleships;

import gui.std.GBackgroundedPanel;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JTextField;

import app.Client;
import app.Request;
import app.User;

@SuppressWarnings("serial")
public class GBattlePanel extends JPanel {
	
	public GBattlePanel(Client c, User u) {
		env = new BoxEnvironment(c);
		env.setPhase(SRC_LOC);
		env.setImage(u.getImage().getImage());
		initialize();
	}

	private void initialize() {
		setLayout(new GridLayout(ROWS, COLS));
		for (int i = 0; i < ROWS; i++) {
			for (int j = 0; j < COLS; j++) {
				BattleBox bb = new BattleBox(env, i, j);
				env.environment[i][j] = bb;
				add(bb); 
			}
		}
		
		//doesn't work at all
		addKeyListener(new KeyAdapter() {
			public void keyTyped(KeyEvent e) {
				System.out.println("typed");
			}
		});
	}
	
	public void setPhase(int p) {
		env.setPhase(p);
	}
	
	public void setDisplay(JTextField d) {
		env.setDisplay(d);
	}
	
	public void setId(long id) {
		env.setId(id);
	}
	
	public boolean showRequest(Point p) {
		BattleBox b = env.environment[p.x][p.y];
		boolean r = b.isUsed();
		
		b.showImage(null);
		b.setBackground(BattleBox.BACKGROUND);
		b.updateUI();
		return r;
	}
	
	public void showResponse(boolean b) {
		env.getSaved().setUsed(true);
		if (b) {
			env.getSaved().showImage();
		}
	}
	
	public static int getTotalPoints() {
		int s = 0;
		for (int i = 0; i < sizes.length -1; i++)
			s += sizes[i];
		return s;
	}
	
	public void hold() {
		env.hold();
	}
	
	public static final int SRC_LOC = 0;
	public static final int TRG_LOC	= 1;
	public static final int WAITING = 3;
	public static final int ATTACK = 4;
	public static final int HELD = 5;

	public static final int ROWS = 10;
	public static final int COLS = 10;
	public static final int[] sizes = {5, 4, 3, 3, 2, 1, -1};
	
	private BoxEnvironment env;
}

@SuppressWarnings("serial")
class BattleBox extends JPanel {
	
	public BattleBox(BoxEnvironment e, int ii, int jj) {
		i = ii;
		j = jj;
		used = false;
		env = e;
		setLayout(new BorderLayout());
		setBorder(BorderFactory.createLineBorder(Color.BLUE, 1));
		setBackground(BACKGROUND);
		
		bp = new GBackgroundedPanel(null);
		add(bp);
		
		addMouseListener(new MouseAdapter() {
			public void mouseEntered(MouseEvent e) {
				showShadow();
			}
			
			public void mouseExited(MouseEvent e) {
				hideShadow();
			}
			
			public void mouseClicked(MouseEvent e) { 
				boxClicked();
			}
		});
	}
	
	private boolean isValid(int i, int j) {
		BattleBox b[][] = env.environment;
		
		if ((i > 0) && (j > 0))
			if (b[i - 1][j - 1].isUsed())
				return false;
		
		if (i > 0)
			if (b[i - 1][j].isUsed())
				return false;
			
		if ((i > 0) && (j < GBattlePanel.COLS -1))
			if (b[i - 1][j + 1].isUsed())
				return false;
			
		if (j > 0)
			if (b[i][j - 1].isUsed())
				return false;
		
			if (b[i][j].isUsed())
				return false;
				
		if (j < GBattlePanel.COLS -1)
			if (b[i][j + 1].isUsed())
				return false;
			
		if ((i < GBattlePanel.ROWS - 1) && (j > 0))
			if (b[i + 1][j - 1].isUsed())
				return false;
			
		if (i < GBattlePanel.ROWS -1)
			if (b[i + 1][j].isUsed())
				return false;
		
		if ((i < GBattlePanel.ROWS -1) && (j < GBattlePanel.COLS -1))
			if (b[i + 1][j + 1].isUsed())
				return false;
		
		return true;
	}
	
	private void showShadow() {
		
		switch (env.getPhase()) {
			//source location... setting ships
			case GBattlePanel.SRC_LOC:
				if (isValid(i, j)) {
					setBackground(AVAILABLE);
					
					if ((i - env.getDetail() + 1 >= 0) && (isValid(i - env.getDetail() + 1, j)))
						env.environment[i - env.getDetail() + 1][j].setBackground(SELECTED);
					
					if ((i + env.getDetail() - 1 < env.getHeight()) && (isValid(i + env.getDetail() - 1, j)))
						env.environment[i + env.getDetail() - 1][j].setBackground(SELECTED);
				
					if ((j - env.getDetail() + 1 >= 0) && (isValid(i, j - env.getDetail() + 1)))
						env.environment[i][j - env.getDetail() + 1].setBackground(SELECTED);
					
					if ((j + env.getDetail() - 1 < env.getWidth()) && (isValid(i, j + env.getDetail() - 1)))
						env.environment[i][j + env.getDetail() - 1].setBackground(SELECTED);
				}
				break;
			
			//time to attack
			case GBattlePanel.ATTACK:
				
				if (isUsed())
					setBackground(UNAVAILABLE);
				else
					setBackground(AVAILABLE);
				
				break;
		}
	}
	
	private void hideShadow() {
		
		if (env.getPhase() == GBattlePanel.SRC_LOC) {
			setBackground(BACKGROUND);
			
			if (i - env.getDetail() + 1 >= 0)
				env.environment[i - env.getDetail() + 1][j].setBackground(BACKGROUND);
			
			if (i + env.getDetail() - 1 < env.getHeight())
				env.environment[i + env.getDetail() - 1][j].setBackground(BACKGROUND);
		
			if (j - env.getDetail() + 1 >= 0)
				env.environment[i][j - env.getDetail() + 1].setBackground(BACKGROUND);
			
			if (j + env.getDetail() - 1 < env.getWidth())
				env.environment[i][j + env.getDetail() - 1].setBackground(BACKGROUND);
		}
		else if (isUsed() && (env.getPhase() != GBattlePanel.HELD))
			setBackground(HELD);

		else if ((getBackground() == UNAVAILABLE) || (getBackground() == AVAILABLE) && (env.getPhase() == GBattlePanel.ATTACK))
			setBackground(BACKGROUND);
	}
	
	private void locateShip() {
		if (env.getSaved() != null) {
			BattleBox a = env.getSaved();
			if (a.i != i) {
				if (a.i > i) {
					for (int ii = i; ii <= a.i; ii++) {
						env.environment[ii][j].showImage();
						env.environment[ii][j].bp.updateUI();
					}
				}
				else {
					for (int ii = i; ii >= a.i; ii--) {
						env.environment[ii][j].showImage();
						env.environment[ii][j].bp.updateUI();
					}
				}
			}
			else {
				if (a.j > j) {
					for (int jj = j; jj <= a.j; jj++) {
						env.environment[i][jj].showImage();
						env.environment[i][jj].bp.updateUI();
					}
				}
				else {
					for (int jj = j; jj >= a.j; jj--) { 
						env.environment[i][jj].showImage();
						env.environment[i][jj].bp.updateUI();
					}
				}
			}
		}
	}
	
	private void boxClicked() {
		
		if (env.getPhase() == GBattlePanel.SRC_LOC) {
			env.setSaved(this);
			env.setPhase(GBattlePanel.TRG_LOC);
		}
		else if ((env.getPhase() == GBattlePanel.TRG_LOC) && (getBackground() == SELECTED)) {
			env.setPhase(GBattlePanel.SRC_LOC);
			env.getSaved().hideShadow();
			
			//Setting ship location
			locateShip();
			env.incDetail();
			
			if (env.getDetail() < 0) {
				//target user id = game id
				Request r = new Request(env.getClient().getUser(), new User(env.getId()));
				r.setType(Request.DONE);
				r.setMsg(env.getBoard());
								
				try {
					env.getClient().chatSend(r);
				} 
				catch (IOException e) {e.printStackTrace();}
				
				//holding board
				env.hold();
				env.showMessage("Done!! Waiting for server response ...");
				
			}
		}
		//asking for position to server
		else if ((env.getPhase() == GBattlePanel.ATTACK) && (!isUsed())) {
			//target user id = game id
			Request r = new Request(env.getClient().getUser(), new User(env.getId()));
			r.setType(Request.POS_REQUEST);
			r.setMsg(new Point(i, j));
			env.setSaved(this);
							
			try {
				env.getClient().chatSend(r);
			} 
			catch (IOException e) {e.printStackTrace();}
		}
		else if (env.getPhase() != GBattlePanel.HELD)
			setBackground(UNAVAILABLE);
	}
	
	public void showImage() {
		used = true;
		bp.setImage(env.getImage());
	}
	
	public void showImage(Image i) {
		used = true;
		bp.setImage(i);
	}

	public boolean isUsed() {
		return used;
	}
	
	public void setUsed(boolean b) {
		used = b;
	}
	
	public BoxEnvironment getEnvironment() {
		return env;
	}
	
	public static final Color BACKGROUND = new Color(96, 190, 230);
	public static final Color HELD = new Color(98, 170, 150);
	public static final Color AVAILABLE = Color.GREEN.brighter();
	public static final Color UNAVAILABLE = Color.RED.brighter();
	public static final Color SELECTED = Color.ORANGE;

	private int i;
	private int j;
	private boolean used;
	private GBackgroundedPanel bp;
	private BoxEnvironment env;
}

class BoxEnvironment {
	
	public BoxEnvironment(Client c) {
		client = c;
		detail = GBattlePanel.sizes[ind = 0];
		environment = new BattleBox[width][height];
		initialize();
	}

	private void initialize() {
		for (int i = 0; i < width; i++)
			for (int j = 0; j < height; j++)
				environment[i][j] = null;
	}
	
	public void setDimension(int w, int h) {
		width = w;
		height = h;
		initialize();
	}
	
	public void setImage(Image i) {
		img = i;
	}
	
	public void setPhase(int phase) {
		this.phase = phase;
	}
	
	public void setSaved(BattleBox saved) {
		this.saved = saved;
	}
	
	public void setDisplay(JTextField d) {
		display = d;
	}
	
	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}
	
	public Image getImage() {
		return img;
	}
	
	public int getPhase() {
		return phase;
	}

	public int getDetail() {
		return detail;
	}
	
	public void setDetail(int d) {
		detail = d;
	}

	public void incDetail() {
		detail = GBattlePanel.sizes[++ind];
	}

	public BattleBox getSaved() {
		return saved;
	}
	
	public Client getClient() {
		return client;
	}
	
	public boolean[][] getBoard() {
		boolean [][] tr = new boolean[GBattlePanel.ROWS][GBattlePanel.COLS];
		
		for (int i = 0; i < GBattlePanel.ROWS; i++)
			for (int j = 0; j < GBattlePanel.COLS; j++) 
				tr[i][j] = environment[i][j].isUsed();
		
		return tr;
	}
	
	public void showMessage(String string) {
		display.setText(string);
	}
	
	public void setId(long i) {
		id = i;
	}
	
	public long getId() {
		return id;
	}
	
	public void hold() {
		setPhase(GBattlePanel.HELD);
		for (int i = 0; i < GBattlePanel.ROWS; i++)
			for (int j = 0; j < GBattlePanel.COLS; j++)
				environment[i][j].setBackground(BattleBox.HELD);
	}
	
	private int width = GBattlePanel.COLS;
	private int height = GBattlePanel.ROWS;
		
	public BattleBox environment[][];
	
	private int phase;
	private int ind;
	private int detail;
	private long id;
	private Image img;
	private BattleBox saved;
	private JTextField display;
	private Client client;
}